### Eclipse Workspace Patch 1.0
#P OpenConcerto
Index: src/org/openconcerto/erp/core/sales/pos/ui/POSButton.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/POSButton.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/POSButton.java	(working copy)
@@ -19,15 +19,68 @@
 import javax.swing.JButton;
 
 public class POSButton extends JButton {
+	private Color enabledBackgroundColor = null;
+	private Color disabledBackgroundColor = null;
+	
     public POSButton(String label) {
         super(label);
         final Font f = getFont().deriveFont(20f);
         this.setForeground(Color.WHITE);
         this.setBackground(CaissePanel.DARK_BLUE);
+        this.setDisabledBackground(new Color(0xBCD3EF));
         this.setFont(f);
         this.setFocusPainted(false);
         this.setBorderPainted(false);
         this.setContentAreaFilled(false);
         this.setOpaque(true);
     }
+
+
+    /**
+     * Enables (or disables) the button changing his background color.
+     * @param b  true to enable the button, otherwise false
+     */
+    @Override
+    public void setEnabled(boolean b) {
+        super.setEnabled(b);
+        if (b) {
+        	if  ((enabledBackgroundColor != null) && !(enabledBackgroundColor.equals(this.getBackground()))) {
+        		super.setBackground(enabledBackgroundColor);
+        	}
+        }else {
+        	if  ((disabledBackgroundColor != null) && !(disabledBackgroundColor.equals(this.getBackground()))) {
+        		super.setBackground(disabledBackgroundColor);
+        	}
+        }
+    }
+
+    /**
+     * Sets the background color of this component.
+     * <p>
+     * The background color affects each component differently and the
+     * parts of the component that are affected by the background color
+     * may differ between operating systems.
+     *
+     * @param c the color to become this component's color;
+     *          if this parameter is <code>null</code>, then this
+     *          component will inherit the background color of its parent
+
+     */
+    @Override
+	public void setBackground(Color bg) {
+		super.setBackground(bg);
+		if (this.isEnabled()) {
+			enabledBackgroundColor = this.getBackground();
+		} else {
+			disabledBackgroundColor = this.getBackground();
+		}
+	}
+
+	public void setDisabledBackground(Color bg) {
+		if (bg == null) {
+			disabledBackgroundColor = this.getBackground();
+		} else {
+			disabledBackgroundColor = bg;
+		}
+	}
 }
Index: src/org/openconcerto/erp/core/sales/pos/ui/PriceEditorPanel.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/PriceEditorPanel.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/PriceEditorPanel.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  * 
  * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
  * 
@@ -14,11 +14,13 @@
  package org.openconcerto.erp.core.sales.pos.ui;
 
 import org.openconcerto.erp.core.common.ui.NumericTextField;
+import org.openconcerto.erp.core.finance.tax.model.TaxeCache;
 import org.openconcerto.erp.core.sales.pos.model.Article;
 
 import java.awt.Color;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
+import java.awt.GridLayout;
 import java.awt.Insets;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -26,6 +28,7 @@
 
 import javax.swing.ButtonGroup;
 import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 
@@ -38,6 +41,7 @@
     private NumericTextField ttcTextField;
     private NumericTextField discountPercentTextField;
     private NumericTextField discountTextField;
+    private NumericKeypadPanel keyPad;
 
     public PriceEditorPanel(final CaisseFrame caisseFrame, final Article article) {
         this.article = article;
@@ -49,7 +53,7 @@
         c.fill = GridBagConstraints.HORIZONTAL;
         c.anchor = GridBagConstraints.EAST;
         c.weightx = 0;
-        c.weighty = 0;
+        c.weighty = 1;
         c.gridx = 0;
         c.gridy = 0;
         c.insets = new Insets(20, 20, 30, 20);
@@ -60,28 +64,29 @@
         // Line 2
         c.gridy++;
         c.gridwidth = 1;
-        rHT = new POSRadioButton("prix HT");
-        rHT.setSelected(true);
+        rTTC = new POSRadioButton("prix TTC");
+        rTTC.setSelected(true);
         c.weightx = 0;
-        this.add(rHT, c);
-        htTextField = new NumericTextField();
-        htTextField.setValue(article.getPriceWithoutTax());
-        htTextField.setFont(title.getFont());
+        this.add(rTTC, c);
+        ttcTextField = new NumericTextField();
+        ttcTextField.setFont(title.getFont());
+        ttcTextField.setValue(article.getPriceWithTax());
+        ttcTextField.requestFocusInWindow();// This will set focus on the text field
         c.gridx++;
         c.weightx = 1;
-        this.add(htTextField, c);
+        this.add(ttcTextField, c);
         // Line 3
         c.gridy++;
-        rTTC = new POSRadioButton("prix TTC");
-        htTextField.setValue(article.getPriceWithTax());
+        rHT = new POSRadioButton("prix HT");
         c.gridx = 0;
         c.weightx = 0;
-        this.add(rTTC, c);
-        ttcTextField = new NumericTextField();
-        ttcTextField.setFont(title.getFont());
+        this.add(rHT, c);
+        htTextField = new NumericTextField();
+        htTextField.setValue(article.getPriceWithoutTax());
+        htTextField.setFont(title.getFont());
         c.gridx++;
         c.weightx = 1;
-        this.add(ttcTextField, c);
+        this.add(htTextField, c);
         // Line 4
         c.gridy++;
         rDiscountPercent = new POSRadioButton("remise en %");
@@ -118,9 +123,11 @@
         c.gridx = 0;
         c.gridwidth = 2;
         final POSLabel labelPriceOld = new POSLabel("Ancien Prix : ");
-        final BigDecimal ttc = Article.computePriceWithTax(this.article.getPriceWithoutTax(), this.article.getIdTaxe());
-        ttcTextField.setValue(ttc);
-        labelPriceOld.setText("Ancien Prix : " + TicketCellRenderer.toString(this.article.getPriceWithoutTax()) + " HT, " + TicketCellRenderer.toString(ttc));
+//        final BigDecimal ttc = Article.computePriceWithTax(this.article.getPriceWithoutTax(), this.article.getIdTaxe());
+//        ttcTextField.setValue(ttc);
+        labelPriceOld.setText("Ancien Prix : " 
+        		+ TicketCellRenderer.toString(this.article.getPriceWithTax()) + "€ TTC  ,  "
+        		+ TicketCellRenderer.toString(this.article.getPriceWithoutTax()) + "€ HT");
         this.add(labelPriceOld, c);
 
         c.gridy++;
@@ -133,12 +140,12 @@
         c.fill = GridBagConstraints.NONE;
         c.anchor = GridBagConstraints.SOUTHEAST;
 
-        final JPanel buttons = new JPanel();
+        final JPanel buttons = new JPanel(new GridLayout(1, 2, 10, 0));
         buttons.setOpaque(false);
-        POSButton bApply = new POSButton("Appliquer");
-        buttons.add(bApply, c);
         POSButton bCancel = new POSButton("Annuler");
         buttons.add(bCancel, c);
+        POSButton bApply = new POSButton("Appliquer");
+        buttons.add(bApply, c);
         bApply.addActionListener(new ActionListener() {
 
             @Override
@@ -156,6 +163,18 @@
         });
 
         this.add(buttons, c);
+        
+        c.anchor = GridBagConstraints.CENTER;
+        c.weightx = 0;
+        c.weighty = 0;
+        c.gridx = 3;
+        c.gridy = 2;
+        c.insets = new Insets(20, 20, 30, 20);
+        // Line 1
+        c.gridheight = 5;
+        keyPad = new NumericKeypadPanel(ttcTextField);
+        this.add(keyPad, c);
+
         updatePrice(article.getPriceWithoutTax());
         updateTextFields();
         //
@@ -197,6 +216,12 @@
         this.htTextField.getDocument().addDocumentListener(docListener);
         this.discountPercentTextField.getDocument().addDocumentListener(docListener);
         this.discountTextField.getDocument().addDocumentListener(docListener);
+        
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                updateTextFields() ;// This will set focus on the text field
+            }
+          });
     }
 
     protected BigDecimal getHTFromUI() {
@@ -205,7 +230,7 @@
             if (this.rHT.isSelected()) {
                 r = this.htTextField.getValue();
             } else if (this.rTTC.isSelected()) {
-                r = Article.computePriceWithoutTax(this.ttcTextField.getValue(), this.article.getIdTaxe());
+            	r = Article.computePriceWithoutTax(this.ttcTextField.getValue(), this.article.getIdTaxe());
             } else if (this.rDiscountPercent.isSelected()) {
                 r = this.article.getPriceWithoutTax().subtract(this.article.getPriceWithoutTax().multiply(this.discountPercentTextField.getValue().divide(new BigDecimal(100))));
             } else if (this.rDiscount.isSelected()) {
@@ -223,7 +248,10 @@
 
     void updatePrice(BigDecimal ht) {
         BigDecimal ttc = Article.computePriceWithTax(ht, this.article.getIdTaxe());
-        labelPrice.setText("Nouveau Prix : " + TicketCellRenderer.toString(ht) + " HT, " + TicketCellRenderer.toString(ttc));
+        labelPrice.setText("Nouveau Prix : "
+        		+ TicketCellRenderer.toString(ttc) + "€ TTC  ,  "
+        		+ TicketCellRenderer.toString(ht) + "€ HT");
+        
     }
 
     public void updateTextFields() {
@@ -234,12 +262,20 @@
         discountTextField.setVisible(false);
         if (rHT.isSelected()) {
             htTextField.setVisible(true);
+            htTextField.requestFocusInWindow();//requestFocus();  is discouraged because its behavior is platform dependent. // Set the focus to the input field
+            keyPad.setDataSource(htTextField);
         } else if (rTTC.isSelected()) {
             ttcTextField.setVisible(true);
-        } else if (rDiscountPercent.isSelected()) {
+            ttcTextField.requestFocusInWindow();//requestFocus();  is discouraged because its behavior is platform dependent. // Set the focus to the input field
+            keyPad.setDataSource(ttcTextField);
+       } else if (rDiscountPercent.isSelected()) {
             discountPercentTextField.setVisible(true);
+            discountPercentTextField.requestFocusInWindow();//requestFocus();  is discouraged because its behavior is platform dependent. // Set the focus to the input field
+            keyPad.setDataSource(discountPercentTextField);
         } else if (rDiscount.isSelected()) {
             discountTextField.setVisible(true);
+            discountTextField.requestFocusInWindow();//requestFocus();  is discouraged because its behavior is platform dependent. // Set the focus to the input field
+            keyPad.setDataSource(discountTextField);
         }
         this.validate();
         repaint();
Index: src/org/openconcerto/erp/core/sales/pos/ui/NumericKeypadPanel.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/NumericKeypadPanel.java	(nonexistent)
+++ src/org/openconcerto/erp/core/sales/pos/ui/NumericKeypadPanel.java	(working copy)
@@ -0,0 +1,264 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ * 
+ * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
+ * 
+ * The contents of this file are subject to the terms of the GNU General Public License Version 3
+ * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
+ * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
+ * language governing permissions and limitations under the License.
+ * 
+ * When distributing the software, include this License Header Notice in each file.
+ */
+ 
+ package org.openconcerto.erp.core.sales.pos.ui;
+
+import org.openconcerto.erp.core.common.ui.NumericTextField;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.math.BigDecimal;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFrame;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.KeyStroke;
+
+public class NumericKeypadPanel extends JPanel {
+
+    private NumericTextField dataSource;
+    private Map<String, POSButton> buttonMap = new HashMap<>();
+    private NumericKeyListener evt = new NumericKeyListener();
+    private int maxDecimalDigits = 2;
+    private static final int INSET = 2;
+    private static final int PANEL_HEIGHT = (68 * 5) + (INSET * 4);
+    private static final int PANEL_WIDTH = (68 * 3) + (INSET * 2);
+    private static final int BUTTON_SIZE = 64;
+ 
+
+    public NumericKeypadPanel() {
+    	super();
+        this.setLayout(new GridBagLayout());
+        drawCalculator();
+    }
+
+    public NumericKeypadPanel(NumericTextField dataSource) {
+    	this();
+    	setDataSource(dataSource);
+   }
+
+    public NumericKeypadPanel(NumericTextField dataSource, int maxDecimalDigits) {
+    	this();
+    	setDataSource(dataSource);
+    	setMaxDecimalDigits(maxDecimalDigits);
+   }
+
+    public final int getMaxDecimalDigits() {
+    	return this.maxDecimalDigits;
+    }
+    
+	public final void setMaxDecimalDigits(int maxDecimalDigits) {
+	    this.maxDecimalDigits = maxDecimalDigits;
+	    updateButtonsValidity();
+	}
+	
+    public final NumericTextField getDataSource() {
+    	return this.dataSource;
+    }
+    
+	public final void setDataSource(NumericTextField dataSource) {
+		if (dataSource != null) {
+		    BigDecimal value = dataSource.getValue();
+			this.dataSource = dataSource;
+		    
+			if (value == null) {
+				this.dataSource.setText("");
+			} else if (Math.abs(value.longValue()) < 0.005) {
+				this.dataSource.setText("");
+			}
+		    updateButtonsValidity();
+		}
+	}
+	
+	private void drawCalculator() {
+        this.setForeground(new Color(250, 250, 250));
+
+        // First line
+        addKey("C", 0, 0, 3, 1).setBackground(CaissePanel.LIGHT_BLUE);
+
+        // second line
+        addKey("7", 0, 1, 1, 1);
+        addKey("8", 1, 1, 1, 1);
+        addKey("9", 2, 1, 1, 1);
+
+        // third
+        addKey("4", 0, 2, 1, 1);
+        addKey("5", 1, 2, 1, 1);
+        addKey("6", 2, 2, 1, 1);
+
+        // Fourth line
+        addKey("1", 0, 3, 1, 1);
+        addKey("2", 1, 3, 1, 1);
+        addKey("3", 2, 3, 1, 1);
+
+        // Fith line
+        addKey("0", 0, 4, 2, 1);
+        addKey(".", 2, 4, 1, 1);
+        // Update PANEL_HEIGHT and PANEL_WIDTH when changing the layout
+    }
+
+    private POSButton addKey(String buttonTitle, int col, int row, int colSpawn, int rowSpawn) {
+        final GridBagConstraints c = new GridBagConstraints();
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.anchor = GridBagConstraints.CENTER;
+        c.weightx = 0;
+        c.weighty = 0;
+        c.gridwidth = colSpawn;
+        c.gridheight = rowSpawn;
+        c.gridx = col;
+        c.gridy = row;
+        c.insets = new Insets(2, 2, 2, 2);
+
+        POSButton button = new POSButton(buttonTitle);
+        button.setPreferredSize(new Dimension(BUTTON_SIZE * colSpawn, BUTTON_SIZE * rowSpawn));
+        button.addActionListener(evt);
+        buttonMap.put(buttonTitle, button);
+        this.add(button, c);
+        
+        return button;
+    }
+
+    @Override
+    public Dimension getPreferredSize() {
+        return new Dimension(PANEL_WIDTH, PANEL_HEIGHT);
+    }
+
+    @Override
+    public Dimension getMinimumSize() {
+        return new Dimension(PANEL_WIDTH, PANEL_HEIGHT);
+    }
+
+    
+    private void updateButtonsValidity() {
+    	if (dataSource != null) {
+    		final String currentTextValue = dataSource.getText();
+    		final int length = currentTextValue.length();
+    		final int posDecimalSeparator = currentTextValue.indexOf(".");
+    		final boolean hasDecimalSeparator = (maxDecimalDigits == 0) || (posDecimalSeparator > 0);
+    		final boolean hasEnoughtDecimals = hasDecimalSeparator && (posDecimalSeparator < (length -maxDecimalDigits));
+
+    		try {
+    			for (Map.Entry<String, POSButton> entry : buttonMap.entrySet()) {
+    				String k;
+    				POSButton button;
+    				try {
+    					k = entry.getKey();
+    					button = entry.getValue();
+
+    					switch(k) {
+    					case "C" :
+    						button.setEnabled((length > 0));
+    						break;
+
+    					case ".":
+    						button.setEnabled(!hasDecimalSeparator); // Disable the "." button if a dot is already present
+    						break;
+
+    					case "0":
+    					case "1":
+    					case "2":
+    					case "3":
+    					case "4":
+    					case "5":
+    					case "6":
+    					case "7":
+    					case "8":
+    					case "9":
+    						button.setEnabled(!hasEnoughtDecimals);
+    						break;
+    					}
+    				} catch(IllegalStateException ise) {
+    					// this usually means the entry is no longer in the map.
+    					throw new ConcurrentModificationException(ise);
+    				}
+    			}
+
+    		} catch (Exception e) {
+    			e.printStackTrace();
+    		}
+    	}
+    }
+
+    private class NumericKeyListener  implements ActionListener {
+    	@Override
+    	public void actionPerformed(ActionEvent e) {
+    		final String digit = e.getActionCommand(); // Get text from button
+    		String currentTextValue = dataSource.getText();
+
+    		switch(digit) {
+    		case "C" :
+    			final int length = currentTextValue.length();
+    			if (length >0) {
+    				dataSource.setText(currentTextValue.substring(0, length -1));
+    			}
+    			break;
+
+    		case "0":
+    		case "1":
+    		case "2":
+    		case "3":
+    		case "4":
+    		case "5":
+    		case "6":
+    		case "7":
+    		case "8":
+    		case "9":
+    		case ".":
+    			dataSource.setText(currentTextValue + digit);
+    			break;
+    		}
+    		updateButtonsValidity();
+    	}
+    }
+
+    
+    /**
+     * @param args
+     */
+    public static void main(String[] args) {
+        JFrame f = new JFrame();
+        f.setLayout(new GridBagLayout());
+
+        final GridBagConstraints c = new GridBagConstraints();
+
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.anchor = GridBagConstraints.CENTER;
+        c.weightx = 0;
+        c.weighty = 0;
+        NumericTextField testField = new NumericTextField();
+        if(false) {
+        	testField.setValue(new BigDecimal(3.05).setScale(2, BigDecimal.ROUND_HALF_UP));
+        }
+        f.add(testField, c);
+        
+        c.gridx++;
+        NumericKeypadPanel keyPad = new NumericKeypadPanel(testField, 3);
+        f.add(keyPad, c);
+        
+        f.setSize(400, 400);
+        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        f.setVisible(true);
+    }
+
+}
